/* udp_client.c    Creates Internet stream client for a Unix platform.
 The name and port number of the server are passed in as arguments.
 To compile on solaris gcc ... -lnsl -lsocket
 based on in-class handouts,
 getopt, TCP RTT, UDP Bandwidth, and client integration implemented by Mark Pasquier
 UDP RTT  implemented by PBH
 GOOD UDP CLIENT FOR RTT
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <fcntl.h>

#include <time.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

char *msg = "r";
void error(char *msg)
{
  perror(msg);
  exit(0);
}


int main(int argc, char **argv)
{
  int sock, n;
  unsigned short port;

  struct sockaddr_in server;
  struct hostent *hp;
  server.sin_family = AF_INET;

  struct sockaddr_in from;

  char buffer[1400];

  struct timeval start_time;
  struct timeval current_time;
  struct timeval end_time;
  time_t time_result;

  int input = 0;
  int rflag = 0;
  int rvalue = 0;
  int bflag = 0;
  char bvalue[16];
  int tflag = 0;
  int uflag = 0;

  int index = 0;
  long total_bytes = 0;
  int i;


  if (argc < 3)
  {
     printf("Usage: %s server port\n", argv[0]);
     exit(1);
  }


  while ((input = getopt (argc, argv, "r:b:tu")) != -1)
     switch (input)
     {
      case 'r':
        rflag = 1;
        rvalue = atoi(optarg);
        //printf("rvalue= %d\n", rvalue);
        break;
      case 'b':
        strncpy(bvalue, optarg, 16);
        bflag = 1;
        //printf("bvalue= %s\n", bvalue);
        break;
      case 't':
        tflag = 1;
        //printf("T!!\n");
        break;
      case 'u':
        uflag = 1;
        //printf("U!!\n");
        break;
      case '?':
        if ((optopt == 'r') || (optopt == 'b') )
          fprintf (stderr, "Option -%c requires an argument.\n", optopt);
        else if (isprint (optopt))
          fprintf (stderr, "Unknown option `-%c'.\n", optopt);
        else
          fprintf (stderr, "Unknown option character `\\x%x'.\n",optopt);
        return 1;
      default:
        abort ();
      }
/*
  for (index = optind; index < argc; index++)
     printf ("Non-option argument %s : index= %d\n", argv[index],index);
  printf("argv[optind+1]= %s optind= %d\n", argv[optind], optind);
*/
  
  if(!uflag && !tflag){ printf("No Connection Type Specified.\nPlease indicate:\nTCP: -t\nUDP: -u\n\n");}
  if(!rflag && !bflag){ printf("No Test Type Specified.\nPlease indicate:\nBandwidth: -b test_duration\nRTT: -r number_of_cycles\n\n");}
  if(uflag){

          //printf("uflag\n");
          hp = gethostbyname( argv[optind] ); //get hostname/IP from cmd
          if (hp==NULL) error("Unknown host");

          port = atoi( argv[optind+1] )+1; //get portnum
          //printf("port= %d\n", port);

          sock = socket(AF_INET, SOCK_DGRAM, 0);
          if (sock < 0) error("Opening socket");

          memcpy( (char *) &server.sin_addr , (char *) hp->h_addr , hp->h_length);
          server.sin_port = htons(port);
  }//END UDP CONNECTING

////////////UDP RTT///////////////
  if(rflag  && uflag){
          //printf("rflag, rvalue = %d\n", rvalue);
          msg = "r";
          printf("Testing RTT over UDP for %d cycles\n", rvalue);
          time_t total_time = 0;
          for(i=0; i<rvalue; i++){
                  //printf("Started Timer\n");
                  gettimeofday(&start_time, NULL); //start timer

                  n = sendto(sock, msg, strlen(msg), 0, (struct sockaddr *) &server, sizeof server);
                  //printf("Sent message to server\n");

                  if (n < strlen(msg))
                          error("err Writing to socket");

                  //NEED to have an empty sockaddr* to put stuff into
                  int length = sizeof(struct sockaddr_in);
                  n = recvfrom(sock, buffer, 1023,0, (struct sockaddr *) &from, &length);
                  if (n < 1) error("error reading from socket");

                  gettimeofday(&end_time, NULL); //end timer

                  time_result = (1000000 * end_time.tv_sec + end_time.tv_usec)-(1000000 * start_time.tv_sec + start_time.tv_usec);
                  //printf("RTT = %d usec\n", time_result);
                  total_time += time_result;
          }//close sending loop
          printf("  Average RTT: %d usec\n", total_time/rvalue);
          //if (close(sock) < 0) error("closing");
  }//close RTT test



  ////////UDP BANDWIDTH////////////
  if (bflag && uflag)
  {
         printf("Initiating UDP Bandwidth Test for %s Seconds\n", bvalue);
         msg = bvalue;
         int saveflags;
         saveflags=fcntl(sock,F_GETFL,0);
         
         n = sendto(sock, msg, strlen(msg), 0, (struct sockaddr *) &server, sizeof server);
         if (n < strlen(msg)){ error("err Writing to socket");}

         int length = sizeof(struct sockaddr_in);
         n = recvfrom(sock, buffer, 1023,0, (struct sockaddr *) &from, &length);
         if (n < 1) error("error reading from socket");

         gettimeofday(&start_time, NULL);
         total_bytes = 0;
         printf("Receiving...\n");
         do
         {
            n = recvfrom(sock, buffer, 1023,0, (struct sockaddr *) &from, &length);
            if (n < 1) {error("error reading from socket");}
            gettimeofday(&current_time, NULL);
            total_bytes += n;
         } while(
((1000000 * current_time.tv_sec + current_time.tv_usec)-
 (1000000 * start_time.tv_sec   + start_time.tv_usec  ))
 < atoi(bvalue)*1000000);
 
         printf("Total Bytes Received = %d bytes\n", total_bytes);
         float total_time = ((1000000 * current_time.tv_sec + current_time.tv_usec)-(1000000 * start_time.tv_sec + start_time.tv_usec))/1000000;
         printf("Total Time = %2.1f sec\n", total_time);
         printf("Bandwidth = %f MBps\n", total_bytes/total_time/1024/1024);

     //if (close(sock) < 0) error("closing");
  } //end bflag


  
if (rflag && tflag){
   printf("Testing RTT over TCP for %d cycles\n", rvalue);
          time_t total_time = 0;
          for(i=0;i<rvalue;i++){
                  sock = socket(AF_INET, SOCK_STREAM, 0);
                  if (sock < 0){ error("Opening socket");}

                  server.sin_family = AF_INET;
                  hp = gethostbyname(argv[optind]);
                       //printf("argv[optind+1]= %s optind= %d\n", argv[optind], optind);
                  if (hp==NULL) { error("Unknown host");}
                  memcpy((char *)&server.sin_addr,(char *)hp->h_addr,hp->h_length);
                  port = atoi(argv[optind+1]);
                  //printf("port= %d\n", port);
                  server.sin_port = htons(port);
                  if (connect(sock, (struct sockaddr *)&server, sizeof server) < 0)
                     error("Connecting");

                  gettimeofday(&start_time, NULL);

                  n = send(sock, msg, strlen(msg),0);
                  if (n < strlen(msg)){ error("Writing to socket");}

                  n = recv(sock, buffer, 1023,0);
                  if (n < 1) { error("reading from socket");}

                  gettimeofday(&end_time, NULL);
                  buffer[n]='\0';
                  //printf("The message from the server is %s\n",buffer);
                  if (close(sock) < 0){ error("closing");}

                  time_result = (end_time.tv_sec*1000000 + end_time.tv_usec) - (start_time.tv_sec*1000000 + start_time.tv_usec);
                  //printf("RTT= %d usec\n", time_result);
                  total_time += time_result;
                  // printf("Client terminating\n");
               } //end for loop
            printf(" Average RTT: %d usec\n", total_time/rvalue);
  }//end of TCP RTT


  //////////////TCP BANDWIDTH////////////////
  if(bflag && tflag){

          hp = gethostbyname(argv[optind]);
          if (hp==NULL) error("Unknown host");

          //copy IP to another location
          memcpy( (char *) &server.sin_addr, (char *) hp->h_addr, hp->h_length);

          port = atoi(argv[optind+1]); //set port
          server.sin_port = htons(port);

          //open socket
          sock= socket(AF_INET, SOCK_STREAM, 0);
          if (sock < 0) error("Opening socket");

          //connect
          if (connect(sock, (struct sockaddr *)&server, sizeof server) < 0)
                 error("Connecting");

          printf("Initiating TCP Bandwidth Test for %s Seconds\n", bvalue);
          // tell server to start sending packets!
      n = send(sock, bvalue, strlen(bvalue),0);
      if (n < strlen(bvalue))
        error("Writing to socket");

      //confirm the server is sending stuff, but don't count
      n = recv(sock, buffer, 1400, 0);
      if (n < 1) error("Err reading from socket1\n");
      gettimeofday(&start_time, NULL); //start timer

      do
      {
         n = recv(sock, buffer, 1400, 0);
         if (n < 1) error("Err reading from socket2\n");

         total_bytes += n; //tally up the bytes received.
         //printf("dc: %d, n:%d\n", total_bytes, n);
         gettimeofday(&current_time, NULL);
      }
      while ( ( ( current_time.tv_sec*1000000 + current_time.tv_usec) - (  start_time.tv_sec *1000000  + start_time.tv_usec )  ) < atoi(bvalue)*1000000 );
          float total_time = ((( current_time.tv_sec*1000000 + current_time.tv_usec) - (  start_time.tv_sec *1000000  + start_time.tv_usec ))/1000000);
      close(sock);
      //      if (close(sock) < 0) error("closing");
      //time_result = (1000000 * end_time.tv_sec + end_time.tv_usec) - (1000000 * start_time.tv_sec + start_time.tv_usec);


      printf("Total Bytes Received: %d bytes\n", total_bytes);
      double kbytes = total_bytes / (double) 1024.0;
      double mbytes = kbytes / (double) 1024.0;
      //printf("Total KBytes received: %f\n", kbytes);
      //printf("Total MBytes received: %f\n", mbytes);
      printf("Total Time = %2.1f sec\n", total_time);
      //printf("Bandwidth = %f KBps\n", kbytes /  (double) atoi(bvalue) );
      printf("Bandwidth = %f MBps\n", mbytes /  (double) atoi(bvalue) );

  }//END TCP BANDWIDTH




  if(uflag){if (close(sock) < 0) error("closing");}//close UDP connection


  //printf("Client terminating\n");
  return 0;

}//close main


